#include "StdAfx.h"
#include "SessionWatcher.h"

#define RECONNECT_EVENT _T("Global\\%s-%u-Reconnect")
#define DISCONNECT_EVENT _T("Global\\%s-%u-Disconnect")

struct rdc_transport::SessionWatcher::SesssionWatcherImpl
{
    SesssionWatcherImpl(ATL::CString& disconnectName, ATL::CString& reconnectName, SessionWatcher*sWatcher)
        : disconnectEvent_(NULL, FALSE, FALSE, disconnectName.GetBuffer())
        , reconnectEvent_(NULL, FALSE, FALSE, reconnectName.GetBuffer())
        , closeEvent_(FALSE, FALSE)
        , sw_(sWatcher)
        , worker_(boost::bind(&SesssionWatcherImpl::Worker, this))
    {
    }

    ~SesssionWatcherImpl()
    {
        closeEvent_.Set();
        worker_.join();
    }

    void Worker()
    {
        HANDLE hEventConnect[] = 
        {
            disconnectEvent_.m_h,
            reconnectEvent_.m_h,
            closeEvent_.m_h,
        };

        for (bool bContinue = true; bContinue; )
        {
            try
            {
                DWORD dwWait = ::WaitForMultipleObjects(
                    sizeof(hEventConnect) / sizeof(HANDLE),
                    hEventConnect,
                    FALSE,
                    INFINITE);

                switch (dwWait)
                {
                case WAIT_OBJECT_0: // disconnect
                    sw_->m_disconnect();    
                    break;
                case WAIT_OBJECT_0 + 1: // reconnect
                    sw_->m_reconnect();
                    break;
                case WAIT_FAILED:
                    throw std::runtime_error(std::string(__FUNCTION__) + " Error waiting");

                default:
                    bContinue = false;
                }
            }
            catch(std::exception& /*ex*/)
            {
                //TODO:: add error observer here;
            }
            
        }
    }
    
    ATL::CEvent disconnectEvent_;
    ATL::CEvent reconnectEvent_;
    ATL::CEvent closeEvent_;
    SessionWatcher* sw_;
    boost::thread worker_;
};

void rdc_transport::SessionWatcher::AddDisconnectHandler(sessionEventDelegate delegate)
{
    m_disconnect.connect(delegate);
}

void rdc_transport::SessionWatcher::AddReconnectHandler(sessionEventDelegate delegate)
{
    m_reconnect.connect(delegate);
}

rdc_transport::SessionWatcher::SessionWatcher(LPTSTR szAddinRGSName)
{
    DWORD sessionId;
    if (::ProcessIdToSessionId(::GetCurrentProcessId(), &sessionId) == FALSE)
        throw std::runtime_error(std::string(__FUNCTION__) + " ProcessIdToSessionId failed");

    ATL::CString sEventDisconnect;
    sEventDisconnect.Format(DISCONNECT_EVENT, szAddinRGSName, sessionId);

    ATL::CString sEventReconnect;
    sEventReconnect.Format(RECONNECT_EVENT, szAddinRGSName, sessionId);
    
    pimpl.reset(new SesssionWatcherImpl(sEventDisconnect, sEventReconnect, this));
}

rdc_transport::SessionWatcher::~SessionWatcher()
{

}